module addr_gen(
        input   [31:0]  i_rn,
        input   [31:0]  i_rm,
        input   [11:0]  i_imm12,
        input           i_carry,
        input   [ 1:0]  i_addr_sel,
        input   [ 2:0]  i_addr_offset_sel,
        input           i_addr_offset_up,
        input           i_lsm_p_flag,
        input   [ 4:0]  i_lsm_regcnt_tot,
        input   [ 4:0]  i_lsm_regcnt_dec,
        input   [ 3:0]  i_lsm_regcnt_inc,

        output  [31:0]  o_addr,
        output  [31:0]  o_gpr_d0_addr
);

`include "enum.vh"

wire [31:0] ls_shifter_out;
reg  [31:0] addr_offset;
reg  [31:2] addr_offset_lsm;
wire [31:0] addr_wb;
reg  [31:0] addr;

// verilator lint_off PINCONNECTEMPTY
shifter u_shifter_ls(
        .i_op({i_imm12[6:5], 1'b0}),
        .i_rm(i_rm),
        .i_carry(i_carry),
        .i_shamt(i_imm12[11:7]),
        .i_rs(),
        .o_rm_shifted(ls_shifter_out),
        .o_carry()
);
// verilator lint_on PINCONNECTEMPTY

always @* begin
        case ({i_lsm_p_flag, i_addr_offset_up})
                2'b00: addr_offset_lsm = -{25'b0, i_lsm_regcnt_dec}+30'b1;
                2'b01: addr_offset_lsm =  {26'b0, i_lsm_regcnt_inc};
                2'b10: addr_offset_lsm = -{25'b0, i_lsm_regcnt_dec};
                2'b11: addr_offset_lsm =  {26'b0, i_lsm_regcnt_inc}+30'b1;
        endcase
end

always @* begin
        case (i_addr_sel)
                ADDR_SEL_WB  : addr = addr_wb;
                ADDR_SEL_RN  : addr = i_rn;
                ADDR_SEL_LSM : addr = i_rn + {addr_offset_lsm, 2'b0};
                default      : addr = 32'bx;
        endcase
end

always @* begin
        case (i_addr_offset_sel)
                ADDR_OFF_SEL_IMM  : addr_offset = {20'b0, i_imm12};
                ADDR_OFF_SEL_ISH  : addr_offset = ls_shifter_out;
                ADDR_OFF_SEL_IMM8 : addr_offset = {24'b0, i_imm12[11:8], i_imm12[3:0]};
                ADDR_OFF_SEL_RM   : addr_offset = i_rm;
                ADDR_OFF_SEL_LSM  : addr_offset = {25'b0, i_lsm_regcnt_tot, 2'b0};
                default           : addr_offset = 32'bx;
        endcase
end

assign addr_wb = i_addr_offset_up ? i_rn + addr_offset : i_rn - addr_offset ;

assign o_addr = addr;
assign o_gpr_d0_addr = addr_wb;

endmodule
